package cn.xiaobai.utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * 对文件的MD5值进行修改 <br>
 * 可以指定文件的所在路径 <br>
 * 可以指定某个目录下的所有文件或者目录下有某个后缀名的文件 <br>
 * 原理是将唯一的随机字符追加到文件末尾从而从根本上改变文件的原md5值 <br>
 * 如果需要修改的文件或者指定的目录下文件太大或者层级过多的话
 * 可能性能就不是很好了,因为需要扫描到目录下所有的文件,在一个一个进行修改 <br>
 * 单个文件太大也会造成性能上的低效
 *
 * @Author yangdaji
 * @Date 2022/8/20
 * @PackageName cn.xiaobai
 * @ClassName UpdateMd5Test
 */
public class MD5ToolUtils {

    private static Logger log = Logger.getAnonymousLogger();


    ///////////////////// 目录下的所有文件

    /**
     * 修改指定目录下指定后缀文件
     *
     * @param fileDirectory 目录
     * @param endsWith      指定后缀
     */
    public static void batchUpdateDirectory2FileMD5(String fileDirectory, String endsWith) {
        //扫描目录下所有文件
        List<String> pathList = scanFilePath(fileDirectory)
                .stream()
                .filter(i -> i.endsWith(endsWith))
                .collect(Collectors.toList());
        batchUpdateFileMD5(ToolsUtils.toArray(pathList, String.class));
    }


    /**
     * 修改指定目录下指定后缀文件
     *
     * @param fileDirectory 目录
     * @param endsWith      指定后缀
     */
    public static void batchUpdateDirectory2FileMD5(File fileDirectory, String endsWith) {
        //扫描目录下所有文件
        List<String> filePathList = scanFilePath(fileDirectory.getPath())
                .stream()
                .filter(i -> i.endsWith(endsWith))
                .collect(Collectors.toList());
        batchUpdateFileMD5(ToolsUtils.toArray(filePathList, String.class));
    }

    /**
     * 修改指定目录下所有文件
     *
     * @param fileDirectory
     */
    public static void batchUpdateDirectory2FileMD5(String fileDirectory) {
        //扫描目录下所有文件
        List<String> pathList = scanFilePath(fileDirectory);
        batchUpdateFileMD5(ToolsUtils.toArray(pathList, String.class));
    }

    /**
     * 修改指定目录下所有文件
     *
     * @param fileDirectory
     */
    public static void batchUpdateDirectory2FileMD5(File fileDirectory) {
        //扫描目录下所有文件
        List<String> filePathList = scanFilePath(fileDirectory.getPath());
        batchUpdateFileMD5(ToolsUtils.toArray(filePathList, String.class));
    }

    ///////////////////   多文件修改

    /**
     * 批量修改指定的文件md5值
     *
     * @param paths 文件所在位置
     */
    public static void batchUpdateFileMD5(String[] paths) {
        for (String path : paths) {
            updateFileMD5(path);
        }
    }

    /**
     * 批量修改指定的文件md5值
     *
     * @param files
     */
    public static void batchUpdateFileMD5(File[] files) {
        for (File file : files) {
            updateFileMD5(file);
        }
    }

    ///////////////// 单文件修改

    /**
     * 修改指定文件的md5值
     *
     * @param path 文件所在位置
     */
    public static void updateFileMD5(String path) {
        updateFileMD5(new File(path));
    }

    /**
     * 修改指定文件的md5值
     *
     * @param file
     */
    public static void updateFileMD5(File file) {
        writeToFile(file, UuidUtils.getShortUuid()
                , StandardCharsets.UTF_8, true);
    }

    /**
     * 扫描该目录下所有文件
     *
     * @param path 指定目录
     * @return
     */
    public static List<String> scanFilePath(String path) {
        //所有文件的绝对路径
        List<String> absolutePath = new ArrayList<>();
        if (ToolsUtils.isBlank(path)) {
            return absolutePath;
        }
        File sourceFile = new File(path);
        recursionFile(sourceFile.listFiles(), absolutePath);
        return absolutePath;
    }

    /**
     * 扫描该目录下指定后缀的文件
     *
     * @param path 指定目录
     * @return
     */
    public static List<String> scanFilePath(String path, String endsWith) {
        //所有文件的绝对路径
        List<String> absolutePath = new ArrayList<>();
        if (ToolsUtils.isBlank(path)) {
            return absolutePath;
        }
        File sourceFile = new File(path);
        recursionFile(sourceFile.listFiles(), absolutePath, endsWith);
        return absolutePath;
    }

    /**
     * 递归该目录下所有文件
     *
     * @param files
     * @return
     */
    public static List<String> recursionFile(File[] files, List<String> paths) {

        if (ToolsUtils.isNullOrEmpty(files)) {
            return paths;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                recursionFile(file.listFiles(), paths);
            } else if (file.isFile()) {
                paths.add(file.getAbsolutePath());
            }
        }
        return paths;
    }

    /**
     * 递归该目录下指定后缀的文件
     *
     * @param files
     * @return
     */
    public static List<String> recursionFile(File[] files, List<String> paths, String endsWith) {
        if (ToolsUtils.isNullOrEmpty(files)) {
            return paths;
        }
        for (File file : files) {
            if (file.isDirectory()) {
                recursionFile(file.listFiles(), paths);
            } else if (file.isFile() && file.getName().endsWith(endsWith)) {
                paths.add(file.getAbsolutePath());
            }
        }
        return paths;
    }


    public static void writeToFile(String path, String data) {
        writeToFile(new File(path), data, StandardCharsets.UTF_8, false);
    }

    public static void writeToFile(String path, String data, boolean append) {
        writeToFile(new File(path), data, StandardCharsets.UTF_8, append);
    }

    public static void writeToFile(String path, String data, Charset encoding) {
        writeToFile(new File(path), data, encoding, false);
    }

    /**
     * 文件写入目标值
     *
     * @param file     目标文件
     * @param data     目标值
     * @param encoding 字符集
     * @param append   是否文件末尾追加
     */
    public static void writeToFile(File file, String data, Charset encoding, boolean append) {

        try (FileOutputStream out = new FileOutputStream(file, append)) {
            write(out, data, encoding);
        } catch (IOException e) {
            log.info(String.format("写入目标文件错误: %s", e.getLocalizedMessage()));
        }
    }

    /**
     * 文件写入目标值
     *
     * @param out      输出对象
     * @param data     目标值
     * @param encoding 字符集
     */
    public static void write(FileOutputStream out, String data, Charset encoding) throws IOException {
        if (ToolsUtils.isBlank(data)) {
            return;
        }
        out.write(data.getBytes(encoding));
    }

}
